# European option class
import math

from mpmath import norm


class EuropeanOption(object):
    """ Class for European option valuation using Black-Scholes-Merton model.

    Attributes
    ==========
    OptionType : string
        'c' or 'p' for call or put
    S0 : float
        initial stock/index level
    K : float
        strike price
    T : float
        maturity (in year fractions)
    r : float
        constant risk-free short rate
    sigma : float
        volatility factor in diffusion term

    Methods
    =======
    value : float
        return present value of call option
    delta : float
        return delta of call option
    gamma : float
        return gamma of call option
    theta : float
        return theta of call option
    vega : float
        return vega of call option
    pho : float
        return pho of call option
    imp_vol : float
        return implied volatility given option quote
    """

    def __init__(self, OptionType, S0, K, T, r, sigma):
        self.OptionType = OptionType
        self.S0 = float(S0)
        self.K = K
        self.T = T
        self.r = r
        self.sigma = sigma
        self.d1 = ((math.log(self.S0 / self.K) +
                  (self.r + 0.5 * self.sigma ** 2) * self.T) /
                  (self.sigma * math.sqrt(self.T)))
        self.d2 = self.d1 - self.sigma * math.sqrt(self.T)

    def value(self):
        """ Return option value. """
        if self.OptionType == 'c':
            value = (self.S0 * norm.cdf(self.d1) -
                     self.K * math.exp(-self.r * self.T) *
                     norm.cdf(self.d2))
        elif self.OptionType == 'p':
            value = (self.K * math.exp(-self.r * self.T) *
                     norm.cdf(-self.d2) -
                     self.S0 * norm.cdf(-self.d1))
        else:
            print("Error: Option type not valid.")

        return value

    def delta(self):
        """ Return option delta. """
        d1 = ((math.log(self.S0 / self.K) +
               (self.r + 0.5 * self.sigma ** 2) * self.T) /
              (self.sigma * math.sqrt(self.T)))

        if self.OptionType == 'c':
            delta = norm.cdf(d1)
        elif self.OptionType == 'p':
            delta = -norm.cdf(-d1)
        else:
            print("Error: Option type not valid.")

        return delta

    def gamma(self):
        """ Return option gamma """
        d1 = ((math.log(self.S0 / self.K) +
               (self.r + 0.5 * self.sigma ** 2) * self.T) /
              (self.sigma * math.sqrt(self.T)))

        gamma = norm.pdf(d1) / (self.S0 * self.sigma * math.sqrt(self.T))

        return gamma

    def theta(self, timetype='y'):
        """ Return option theta.
         TimeType: 'y' for year, 'm' for month, 'd' for day
         """
        if self.OptionType == 'c':
            theta = (- (self.S0 * norm.pdf(self.d1) * self.sigma) /
                     (2 * math.sqrt(self.T)) -
                     self.r * self.K * math.exp(-self.r * self.T) *
                     norm.cdf(self.d2))
        elif self.OptionType == 'p':
            theta = (- (self.S0 * norm.pdf(self.d1) * self.sigma) /
                     (2 * math.sqrt(self.T)) +
                     self.r * self.K * math.exp(-self.r * self.T) *
                     norm.cdf(-self.d2))
        else:
            print("Error: Option type not valid.")

        if timetype == 'y':
            return theta
        elif timetype == 'm':
            return theta / 12
        elif timetype == 'd':
            return theta / 365
        else:
            print("Error: Time type not valid.")

    def vega(self):
        """ Return option vega. """
        vega = self.S0 * norm.pdf(self.d1) * math.sqrt(self.T)
        return vega

    def pho(self):
        """ Return option pho. """
        if self.OptionType == 'c':
            pho = self.K * self.T * math.exp(-self.r * self.T) * norm.cdf(self.d2)
        elif self.OptionType == 'p':
            pho = -self.K * self.T * math.exp(-self.r * self.T) * norm.cdf(-self.d2)
        else:
            print("Error: Option type not valid.")
        return pho